package des

import (
	"crypto/cipher"
	"crypto/des"

	"github.com/pkg/errors"
)

// IV CBC初始化向量，对数据执行加密转换
var IV = []byte(`BHIcN*G>`)

// EncryptCBC 加密，使用CBC模式
func EncryptCBC(plainText []byte, key []byte) ([]byte, error) {
	block, err := des.NewCipher(key)
	if err != nil {
		return nil, err
	}

	if len(IV) != block.BlockSize() {
		return nil, errors.New("The length of iv must be 8")
	}

	text := paddingPKCS5(plainText)
	cipherText := make([]byte, len(text))

	encrypter := cipher.NewCBCEncrypter(block, IV)
	encrypter.CryptBlocks(cipherText, text)
	return cipherText, nil
}

// EncryptCBC 解密，使用CBC模式
func DecryptCBC(cipherText []byte, key []byte) ([]byte, error) {
	block, err := des.NewCipher(key)
	if err != nil {
		return nil, err
	}

	if len(IV) != block.BlockSize() {
		return nil, errors.New("The length of iv must be 8")
	}

	text := make([]byte, len(cipherText))
	decrypter := cipher.NewCBCDecrypter(block, IV)
	decrypter.CryptBlocks(text, cipherText)
	return unPaddingPKCS5(text), nil
}

// EncryptCBCTriple 加密，使用三重数据加密算法（3DES），CBC模式
// key的长度必须是 16 或 24 bytes
func EncryptCBCTriple(plainText []byte, key []byte) ([]byte, error) {
	if !(len(key) == 16 || len(key) == 24) {
		return nil, errors.New("key length must be 16 or 24")
	}

	var newKey []byte
	if len(key) == 16 {
		newKey = append([]byte{}, key...)
		newKey = append(newKey, key[:8]...)
	} else {
		newKey = append([]byte{}, key...)
	}

	block, err := des.NewTripleDESCipher(newKey)
	if err != nil {
		return nil, err
	}

	if len(IV) != block.BlockSize() {
		return nil, errors.New("The length of iv must be 8")
	}

	text := paddingPKCS5(plainText)
	cipherText := make([]byte, len(text))
	encrypter := cipher.NewCBCEncrypter(block, IV)
	encrypter.CryptBlocks(cipherText, text)

	return cipherText, nil
}

// DecryptCBCTriple 解密，使用三重数据加密算法（3DES），CBC模式
// key的长度必须是 16 或 24 bytes
func DecryptCBCTriple(cipherText []byte, key []byte) ([]byte, error) {
	if !(len(key) == 16 || len(key) == 24) {
		return nil, errors.New("key length must be 16 or 24")
	}

	var newKey []byte
	if len(key) == 16 {
		newKey = append([]byte{}, key...)
		newKey = append(newKey, key[:8]...)
	} else {
		newKey = append([]byte{}, key...)
	}

	block, err := des.NewTripleDESCipher(newKey)
	if err != nil {
		return nil, err
	}

	if len(IV) != block.BlockSize() {
		return nil, errors.New("The length of iv must be 8")
	}

	text := make([]byte, len(cipherText))
	decrypter := cipher.NewCBCDecrypter(block, IV)
	decrypter.CryptBlocks(text, cipherText)

	return unPaddingPKCS5(text), nil
}
